using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Xaml.Behaviors;

/*
 * 代码：https://gist.github.com/BYJRK/37d6df1c9f1d198f73538c5167d50f6a
 * 介绍：https://www.bilibili.com/video/BV1E14y137qd/?spm_id_from=pageDriver&vd_source=9b597a6bfcb0f5764169606fb5cd6e6a
 */
namespace WPFTemplateLib.Behaviors
{
	/// <summary>
	/// Allows a <see cref="Panel"/> or <see cref="ItemsControl"/> to have a <see cref="ComboBox"/>-like behavior
	/// that has a <see cref="SelectedItem"/> dependency property to be bound to a property in view model. <para/>
	/// [行为] 给包含一系列 RadioButton 的 Panel 或 ItemsControl 引入一个可绑定的 SelectedItem 属性（RadioButton 的值需存放在 Tag 中）
	/// </summary>
	/// <example>
	/// <code lang="xaml">
	/// <![CDATA[
	/// xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
	/// xmlns:be="clr-namespace:WpfTutorials.RadioButtonBindings.Behaviors"
	/// 
	/// <StackPanel>
	///     <i:Interaction.Behaviors>
	///         <be:SelectedItemBehavior SelectedItem="{Binding SelectedItem}" />
	///     </i:Interaction.Behaviors>
	///     <RadioButton Tag="A" />
	///     <RadioButton Tag="B" />
	///     <RadioButton Tag="C" />
	/// </StackPanel>
	/// ]]>
	/// </code>
	/// </example>
	public class SelectedItemBehavior : Behavior<FrameworkElement>
	{
		#region SelectedItem Dependency Property

		public object SelectedItem
		{
			get => GetValue(SelectedItemProperty);
			set => SetValue(SelectedItemProperty, value);
		}

		/// <summary>
		/// The <see cref="SelectedItem"/> within this <see cref="Panel"/> or <see cref="ItemsControl"/>
		/// </summary>
		public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
			nameof(SelectedItem),
			typeof(object),
			typeof(SelectedItemBehavior),
			new PropertyMetadata(null, OnSelectedItemChanged)
		);

		private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var behavior = Interaction.GetBehaviors(d).OfType<SelectedItemBehavior>().FirstOrDefault();
			if (behavior is null) return;

			foreach (var child in behavior.Children)
			{
				child.IsChecked = child.Tag.Equals(e.NewValue);
			}
		}

		#endregion

		/// <inheritdoc />
		protected override void OnAttached()
		{
			AssociatedObject.Loaded += OnAssociatedLoaded;
		}

		/// <inheritdoc />
		protected override void OnDetaching()
		{
			AssociatedObject.Loaded -= OnAssociatedLoaded;
			foreach (var child in Children)
			{
				child.Checked -= OnRadioButtonChecked;
			}
		}

		private void OnAssociatedLoaded(object sender, RoutedEventArgs e)
		{
			foreach (var child in Children)
			{
				child.Checked += OnRadioButtonChecked;
				child.IsChecked = child.Tag.Equals(SelectedItem);
			}
		}

		private void OnRadioButtonChecked(object sender, RoutedEventArgs e)
		{
			var radio = sender as RadioButton;
			if(radio == null) return;

			SelectedItem = radio.Tag;
			BindingOperations.GetBindingExpression(this, SelectedItemProperty)?.UpdateSource();
		}

		/// <summary>
		/// Enumerate all <see cref="RadioButton"/> children of the <see cref="Behavior{T}.AssociatedObject"/>
		/// </summary>
		private IEnumerable<RadioButton> Children
		{
			get
			{
				switch (AssociatedObject)
				{
					case Panel panel:
						return panel.Children.OfType<RadioButton>();
					case ItemsControl control:
						return control.Items.OfType<RadioButton>();
					default:
						return Enumerable.Empty<RadioButton>();
				}
			}
		}
	}
}
